# -*- coding: utf-8 -*-
import binascii
import importlib
import sys
import pickle
import time

from PySide6.QtCore import Signal, QTimer, Qt
from PySide6.QtGui import QShortcut, QKeySequence, QTextCursor
from PySide6.QtWidgets import QWidget, QMessageBox, QApplication, QFileDialog
from loguru import logger

from utils.bytesHelper import SliceBytesSafe
from utils.checksum import hex_string_to_list, get_tcon_checksum, i2c0_get_checksum
from utils.filePathTools import GetFilesWithExtension
from windowUI.gWMEngForwardDataUI_ui import Ui_GWMEngForwardDataForm


class GWMEngForwardDataWindow(QWidget, Ui_GWMEngForwardDataForm):
    """
    工程模式数据转发与读取
    """
    # 声明带一个字典类型参数的信号
    engForwardDataSignal = Signal(dict)
    needSend = False
    execDelay = 1500  # ms

    # 调节阻尼设置

    def __init__(self):
        super().__init__()
        # 加载UI
        self.setupUi(self)
        # 连接信号与槽
        self.SignalConnectInit()
        # 创建定时器
        self.TimerInit()
        # 添加快捷键
        self.ShortcutFun()
        # 引入公共程序
        from utils.common import commonProgram
        # 引入配置文件
        self.myConfig = commonProgram.EnableConfigHandler()
        print('TpIntWindow get config Version', self.myConfig.version)
        # 引入可控串口信息窗口
        self.serialPortWin = commonProgram.EnableFt4222(winSignal=self.engForwardDataSignal,
                                                        RecvFt4222Data=self.RecvFt4222Data)
        # 显示窗口
        # self.serialPortWin.show()

    def CleanupSelf(self):
        """
        清理当前窗口并关闭，由其他其他程序调用
        :return:
        """
        try:
            # 停止并注销定时器
            self.timer_delay.stop()
            self.timer_delay.deleteLater()
            self.timer_exec.stop()
            self.timer_exec.deleteLater()
            self.close()  # 关闭当前窗口
            self.deleteLater()  # 释放子窗口资源
            print("TpIntWindow.__del__.ok")
        except Exception as err:
            print("TpIntWindow.__del__.error", err)

    def SignalConnectInit(self):
        """
        连接来自UI对象的信号与槽
        :return:
        """
        self.checkBoxCycleSend.stateChanged.connect(self.CheckBoxCycleSendFun)
        self.checkBoxEngMode.stateChanged.connect(self.CheckBoxEngModeFun)
        self.btnOnceSend.clicked.connect(self.BtnOnceSendFun)
        self.btnSave.clicked.connect(self.SaveLogsFun)
        self.btnClean.clicked.connect(self.CleanLogsFun)

    def BtnOnceSendFun(self):
        if not self.checkBoxEngMode.isChecked():
            QMessageBox.warning(self, "需要工程模式", f"请 先勾选工程模式！！！")
            self.checkBoxCycleSend.setChecked(False)
            return None
        slaveAddr = self.lineEditI2cAddr.text().strip()
        if (len(slaveAddr) < 1) or (not ("0x" in slaveAddr)):
            QMessageBox.warning(self, "空数据", f"请输入正确的设备地址！--> {slaveAddr}")
            return None
        modeText = self.comboBoxModeSelect.currentText()
        if '手动' in modeText:
            if self.checkBoxSelectWrite.isChecked():
                self.EngWriteFun()
            if self.checkBoxSelectGetResult.isChecked():
                dataStr = "FF"
                time.sleep(0.005)
                self.ReadDataFun(read_data=dataStr)
            if self.checkBoxSelectExWrite.isChecked():
                self.EngExWriteFun()
            if self.checkBoxSelectGetResult.isChecked():
                dataStr = "FF"
                time.sleep(0.005)
                self.ReadDataFun(read_data=dataStr)
            if self.checkBoxSelectRead.isChecked():
                self.EngReadFun()
            if self.checkBoxSelectGetResult.isChecked():
                dataStr = "FF"
                time.sleep(0.005)
                self.ReadDataFun(read_data=dataStr)

    def SendDataFun(self, send_data="00"):
        # hex发送
        input_s = send_data
        send_list = []

        while input_s != '':
            try:
                num = int(input_s[0:2], 16)
            except ValueError:
                # QMessageBox.critical(self, 'wrong data', '请输入十六进制数据，以空格分开!')
                return None
            input_s = input_s[2:].strip()
            send_list.append(num)
        input_s = bytes(send_list)
        slaveAddr = self.lineEditI2cAddr.text().strip()
        i2cData = {
            "slaveAddr": eval(slaveAddr),
            "writeData": input_s,
        }
        logger.debug(eval(slaveAddr))
        logger.debug(input_s)
        dictData = {"data": i2cData, "type": "send_hex", "options": {"raw_data": send_data}}
        self.SendDataHandle(dictData)
        self.UIPrintFun(f"发>> {send_data}")

    def ReadDataFun(self, read_data="00"):
        slaveAddr = self.lineEditI2cAddr.text().strip()
        input_s = read_data
        send_list = []
        addrLen = 0
        while input_s != '':
            addrLen += 1
            try:
                num = int(input_s[0:2], 16)
            except ValueError:
                # QMessageBox.critical(self, 'wrong data', '请输入十六进制数据，以空格分开!')
                return None
            input_s = input_s[2:].strip()
            send_list.append(num)
        input_s = bytes(send_list)
        i2cData = {
            "slaveAddr": eval(slaveAddr),
            "dataAddr": input_s,
            "dataLen": 33,
        }
        dictData = {"data": i2cData, "type": f"read_hex", "options": {"addrLen": addrLen, }}
        self.SendDataHandle(dictData)

    def EngWriteFun(self):
        dataPrefix = self.lineEditWritePrefix.text().strip()
        dataProtocol = self.comboBoxWriteProtocol.currentText().strip()
        writeTarget = self.lineEditWriteTarget.text().strip()
        frameCount = self.spinBoxWriteFrameCount.value()
        dataLength = self.spinBoxWriteDataLength.value()
        writeData = self.plainTextEditForwardData.toPlainText().strip()
        if len(writeData) < 1:
            QMessageBox.warning(self, "空数据", f"请输入 数据 内容！--> {writeData}")
            return None
        writeData = self.FullData(data=writeData, required_length=23)

        objStr = f"{dataPrefix} {dataProtocol[-2:]} {writeTarget} {frameCount:02X}"
        objStr += ' {:02X} '.format(dataLength)
        objStr += writeData

        if self.checkBoxAutoChecksum.isChecked():
            intListData = hex_string_to_list(objStr)
            # 调用计算函数并获取校验和
            checkSum = i2c0_get_checksum(intListData)
            dataStr = objStr + f" {checkSum:02X}"
            logger.debug(dataStr)
            logger.debug(intListData)
            logger.debug(checkSum)
            self.lineEditWriteChecksum.setText(f"{checkSum:02X}")
        else:
            writeChecksum = self.lineEditWriteChecksum.text().strip()
            dataStr = objStr + f" {writeChecksum}"
            logger.debug(dataStr)
            logger.debug(writeChecksum)
        self.SendDataFun(send_data=dataStr)

    def EngExWriteFun(self):
        dataPrefix = self.lineEditExWritePrefix.text().strip()
        frameIndex = self.spinBoxExWriteIndex.value()
        dataLength = self.spinBoxExWriteDataLength.value()
        writeData = self.plainTextEditExForwardData.toPlainText().strip()
        if len(writeData) < 1:
            QMessageBox.warning(self, "空数据", f"请输入 数据 内容！--> {writeData}")
            return None
        writeData = self.FullData(data=writeData, required_length=23)

        objStr = f"{dataPrefix} {frameIndex:02X} {dataLength:02X} {writeData}"

        if self.checkBoxAutoChecksum.isChecked():
            intListData = hex_string_to_list(objStr)
            # 调用计算函数并获取校验和
            checkSum = i2c0_get_checksum(intListData)
            dataStr = objStr + f" {checkSum:02X}"
            logger.debug(dataStr)
            logger.debug(intListData)
            logger.debug(checkSum)
            self.lineEditExWriteChecksum.setText(f"{checkSum:02X}")
        else:
            writeChecksum = self.lineEditExWriteChecksum.text().strip()
            dataStr = objStr + f" {writeChecksum}"
            logger.debug(dataStr)
            logger.debug(writeChecksum)
        self.SendDataFun(send_data=dataStr)

    def FullData(self, data, required_length):
        # 将数据按空格分割成列表
        data_list = data.split()

        # 判断列表长度是否为22个字节
        # required_length = 22

        if len(data_list) < required_length:
            # 计算需要补齐的个数
            missing_count = required_length - len(data_list)

            # 补齐00
            data_list.extend(["00"] * missing_count)

        # 将列表重新组合成一个字符串
        result = " ".join(data_list).strip()
        return result

    def EngReadFun(self):
        dataPrefix = self.lineEditReadPrefix.text().strip()
        dataProtocol = self.comboBoxReadProtocol.currentText().strip()
        writeTarget = self.lineEditReadTarget.text().strip()
        readOption = self.lineEditReadOption.text().strip()
        dataLength = self.spinBoxReadDataLength.value()
        targetLength = self.spinBoxReadTargetLength.value()
        readData = self.plainTextEditTargetValue.toPlainText().strip()
        if len(readData) < 1:
            QMessageBox.warning(self, "空数据", f"请输入 数据 内容！--> {readData}")
            return None
        readData = self.FullData(data=readData, required_length=22)
        objStr = f"{dataPrefix} {dataProtocol[-2:]} {writeTarget} {readOption} {dataLength:02X} {targetLength:02X} {readData}"

        if self.checkBoxAutoChecksum.isChecked():
            intListData = hex_string_to_list(objStr)
            # 调用计算函数并获取校验和
            checkSum = i2c0_get_checksum(intListData)
            dataStr = objStr + f" {checkSum:02X}"
            logger.debug(dataStr)
            logger.debug(intListData)
            logger.debug(checkSum)
            self.lineEditReadChecksum.setText(f"{checkSum:02X}")
        else:
            writeChecksum = self.lineEditReadChecksum.text().strip()
            dataStr = objStr + f" {writeChecksum}"
            logger.debug(dataStr)
            logger.debug(writeChecksum)
        self.SendDataFun(send_data=dataStr)

    def CheckBoxCycleSendFun(self):
        """
        定时执行
        :return:
        """
        if self.checkBoxCycleSend.isChecked():
            self.timer_delay_count = int(self.spinBoxCycleDelay.text())
            if self.timer_delay_count < 1:
                QMessageBox.critical(self, 'wrong data', '请输入 正确的 延时 ！')
                self.checkBoxCycleSend.setChecked(False)
                return None
            self.timer_delay.start(self.timer_delay_count)
            self.spinBoxCycleDelay.setEnabled(False)
        else:
            self.timer_delay.stop()
            self.spinBoxCycleDelay.setEnabled(True)

    def CheckBoxEngModeFun(self, state):
        logger.debug(state)
        if state:
            dataStr = "FE 5A A5 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 81"
            self.SendDataFun(send_data=dataStr)
            time.sleep(0.1)
            dataStr = "FF"
            self.ReadDataFun(read_data=dataStr)
            self.checkBoxEngMode.setChecked(True)
        else:
            dataStr = "FE 5A A5 81 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 80"
            self.SendDataFun(send_data=dataStr)
            self.checkBoxEngMode.setChecked(False)

    def TimerInit(self):
        """
        定时器创建与信号连接
        此处添加了定时器，一定要在 `CleanupSelf`中添加注销操作，否则切换测试项时不会失效，会一直以子线程运行
        :return:
        """
        self.timer_delay_count = 0
        self.timer_delay = QTimer()
        self.timer_delay.timeout.connect(self.TimeDelayFun)
        self.timer_exec = QTimer()
        self.timer_exec.timeout.connect(self.HandExecFun)
        # self.timer_exec.start(self.execDelay)

    def TimeDelayFun(self):
        self.timer_delay.stop()
        if self.checkBoxCycleSend.isChecked():
            self.btnOnceSend.click()
            self.timer_delay.start(self.timer_delay_count)

    def HandExecFun(self):
        self.timer_exec.stop()
        # self.timer_exec.start(self.execDelay)

    def ShortcutFun(self):
        """
        快捷键 F11 全屏
        快捷键 F5 清屏
        :return:
        """
        # 快捷键 F11
        self.screenStatus = 'Normal'
        screenShortcut = QShortcut(QKeySequence(Qt.Key_F11), self)
        screenShortcut.activated.connect(self.WindowSizeFun)
        self.setToolTip('全屏与退出全屏：\n               双击 或者 F11')
        # 快捷键 F5
        # screenShortcut2 = QShortcut(QKeySequence(Qt.Key_F5), self)
        # screenShortcut2.activated.connect(self.CleanFun)

    def WindowSizeFun(self):
        """
        控制窗口尺寸，此处为原大小（最小UI布局）或者全屏
        :return:
        """
        if self.screenStatus == 'Normal':
            self.showFullScreen()
            self.screenStatus = 'FullScreen'
        else:
            self.showNormal()
            self.screenStatus = 'Normal'

    def SendDataHandle(self, dataDict: dict):
        """
        发送信号或数据处理函数
        :param dataDict:
        :return:
        """
        # 发送信号处理
        dataObj = dataDict.get('data', '')
        typeValue = dataDict.get('type', 'null').strip()
        # 本窗口发送数据
        if "send_" in typeValue:
            self.engForwardDataSignal.emit(dataDict)
        elif "read_" in typeValue:
            self.engForwardDataSignal.emit(dataDict)
        elif "ctrl_gpio2" in typeValue:
            self.engForwardDataSignal.emit(dataDict)
        elif "read_gpio" in typeValue:
            self.engForwardDataSignal.emit(dataDict)
        elif 'TpInt_to_' in typeValue:
            # logger.info("Plugin窗口发往其他窗口的数据：", typeValue, dataObj)
            self.engForwardDataSignal.emit(dataDict)
        elif typeValue != None:
            logger.info(f"dataObj={dataObj}, typeValue={typeValue}")
        # do something

    def RecvFt4222Data(self, dictData: dict):
        """
        用于接收串口控制窗口发过来的信号或数据
        :param dictData:
        :return:
        """
        self.RecvDataHandle(dictData)

    def RecvDataHandle(self, dataDict: dict):
        """
        处理串口控制窗口发过来的信号或数据
        :param dataDict:
        :return:
        """
        dataObj = dataDict.get('data', None)
        typeValue = dataDict.get('type', 'null').strip()
        optionsDict = dataDict.get('options', {})
        # 收到信号
        if 'TpInt_to_' in typeValue:
            return
        if typeValue == 'status_serial':
            if dataObj == "Serial_Window_Showed":
                pass
            elif dataObj == "Serial_Window_Closed":
                pass
        elif typeValue == 'level_gpio':
            pass
        elif typeValue == 'send_hex':
            pass
        elif typeValue == 'read_hex':
            # 使用hexlify函数将bytes数据转换为16进制字符串
            hex_string = binascii.hexlify(dataObj).decode('utf-8').upper()
            # 插入空格
            dataObj = ' '.join(hex_string[i:i + 2] for i in range(0, len(hex_string), 2))
            self.UIPrintFun(f"收<< {dataObj[6:]}")
        elif typeValue == 'rece_protobuf':
            pass
        elif typeValue == 'rece_hex':
            pass
        elif typeValue == 'rece_ascii':
            pass
        elif '_to_TpInt' in typeValue:
            logger.info("收到其他窗口发往Plugin窗口的数据：", typeValue, dataObj)
        # 其他情况
        elif typeValue != None:
            logger.info(f"{typeValue} \t {dataObj}")
        # do something

    def UIPrintFun(self, strData):
        '''
        界面 textBrowser 显示内容
        '''
        # 在 QTextBrowser 中插入格式化的文本
        cursor = self.textBrowser.textCursor()
        # 插入时间文本并应用格式
        cursor.insertText(strData)
        # 换行
        cursor.insertText('\n')
        # 将光标移动到文本末尾
        cursor.movePosition(QTextCursor.End)
        self.textBrowser.setTextCursor(cursor)

    def SaveLogsFun(self):
        try:
            textStr = self.textBrowser.toPlainText()
            # 前面是地址，后面是文件类型,得到输入地址的文件名和地址txt(*.txt*.xls);;image(*.png)不同类别
            # 对话框的父容器parent：一般选为调用对话框的窗体对象。
            # 对话框标题caption：弹出对话框的标题。
            # 文件过滤器filter：选择不同后缀的文件。
            # 初始文件过滤器initialFilter：设置初始的文件过滤器。
            # 对话框options：一个枚举类型。例如：QFileDialog.ShowDirsOnly只显示目录等。
            # 获取到的fileName和filtUsed：是两个元组类型数据。
            filepath, type = QFileDialog.getSaveFileName(self, "打印内容保存", 'data', filter='txt(*.txt)')
            file = open(filepath, 'w')
            # print(filepath, type)
            file.write(textStr)
            file.close()
        except Exception as e:
            pass

    def CleanLogsFun(self):
        self.textBrowser.clear()

    def closeEvent(self, event):
        """
        防止误触关闭程序
        :param event:
        :return:
        """
        # 创建一个确认退出的对话框
        reply = QMessageBox.question(
            self, "Confirmation", "Are you sure you want to exit?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, QMessageBox.StandardButton.No
        )
        if reply == QMessageBox.StandardButton.Yes:
            self.CleanupSelf()
            # 用户确认退出，关闭应用
            event.accept()
        else:
            event.ignore()


# 程序入口，测试当前插件文件使用
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = GWMEngForwardDataWindow()
    myWindow.show()
    sys.exit(app.exec())
